cormoran.me



FPGAのふわっとした紹介

この記事は, KobeUniv Advent Calender 2015 13日目として書かれたものです。

低レイヤーの人とか真の低レイヤーの人とか、とにかく低レイヤーな人がいっぱいいたのでそれに乗っかってFPGAの入門記的な記事を書くことにしました。完全にFPGA初心者ですが…。(もうちょっと先にFPGAの話をするという強そうな人がいるので期待しています。)

FPGAとは?

field-programmable gate array の略らしい。英語そのままですが、プログラミングするだけで論理回路の配線を勝手にやってくれるic的なイメージです。ブレッドボードの配線をプログラミングでできる感じというと分かりやすい? これを使うとお手軽にCPUとかが作れます。詳しくはWikipedia

ハードウェア記述言語

FPGA上に回路を作るにはその回路の設計書を書かないといけません。それにはハードウェア記述言語という専用の言語を使います。いくつか種類があってVerilogやVHDLが有名なようです。C言語っぽいSystemCとかいうのもあります。自分は有名どころであるVerilog,VHDL両方触ってみたのですが、今回はシミュレータの関係と、わかりやすさからVerilogで書きます。

シミュレータ

VerilogにはIcarus Verilogという有名なシミュレータがあります。Windows,Mac,Linuxどれでも動きます。Verilogを書いてIcarus Verilogに渡すと波形データを出力してくれてそれをgtkwaveでみたりできます。web上で動くシミュレータもあります。

Verilogのプログラム

下は4bitFullAdderのプログラム例。+演算子を普通に使ってるのであまり楽しくありませんが。

演算子はc並みに使えてしまうので、それを考えるとbrainf*ckとかよりははるかに高級。

module adder4bit (in1, in2, out, carry);
   input  [3:0] in1, in2;
   output [3:0] out;
   output    carry;
   wire   [4:0] result;

   assign result = in1 + in2;
   assign carry = result[4];
   assign out = result[3:0];
endmodule // adder4bit

シミュレーションプログラムを書いて実行すると

module test();
   reg [3:0] in1, in2;
   wire [3:0] result;
   wire       carry;

   adder4bit adder4bit(in1, in2, result, carry);
   
   initial begin
      $monitor("%t : %b + %b => %b, %b", $time, in1, in2, carry, result);
   end

   initial begin
        in1 <= 4'b0000;
        in2 <= 4'b0000;
      #1
	in1 <= 4'b0001;
        in2 <= 4'b0010;
      #1
	in1 <= 4'b0001;
      	in2 <= 4'b0001;
      #1
	in1 <= 4'b0101;
      	in2 <= 4'b0111;
      #1
	in1 <= 4'b1111;
      	in2 <= 4'b1111;
      #1
	$finish;
   end

endmodule

出力は以下のようになり、ちゃんと足してくれています。

$ iverilog main.v test.v
$ ./a.out 
0 : 0000 + 0000 => 0, 0000
1 : 0001 + 0010 => 0, 0011
2 : 0001 + 0001 => 0, 0010
3 : 0101 + 0111 => 0, 1100
4 : 1111 + 1111 => 1, 1110												

簡単ですね。

なに作ろうか..

低レイヤーに乗っかったのはいいものの、あまり触っていないのでこれ作ったよ!的なものがない…。FPGAに手を出した目的はもちろんCPUを作ることです。実は自分にはトランジスタで論理回路作ろうとか、ICでCPUつくろうとか過去に色々策略を立てては失敗したり気が変わったりしてきた歴史があるのですが、今回こそは作ります。CPUを作るというと、「CPUの創りかた」という有名な本があり、自分も過去に読んだことがあるのですが、この本で紹介されているTD4というCPUならサクッと作れそうです 。(今回作ろうと思っていたが、圧倒的に時間がなかった & 本が借りられなかったので延期)また、有名な話ですが、東大ではFPGAを使ってCPUからコンパイラ、アセンブリまで作る実験があるそうです。コンパイラのコード生成とかの部分はまだ手を出せていませんが興味はあるのでやってみたいですね。あと、最近Prologを習って、WAMというものを知ったのですが、それも面白そうです。大学に飾られてるLispマシンとかも。

で、実はTD4よりはちゃんとした(?)CPUを作ろうと今勉強中なのですが、それはまた今度。何を書こうか考えたのですが、時間もなかったので、すぐに書けるものということで電子サイコロを書いておきました。

電子サイコロ

なぜ電子サイコロ?…実は情報知能工学科二回後期には情報知能工学実験という名のLEGO遊び & ひたすら半田付け の実験があるのですが、半田付けの方の選択課題の一つとして電子サイコロを作れというものがあります。先日ちょうど作ってきました(90分x6コマ)。それにのっける感じで電子サイコロを書くことにしたのですが、半田付けなら540分もかかる回路も、FPGAなら数分で書けます!とりあえずなんか書いた程度なので色々ガバガバです。入力が止まってからしばらく動き続ける仕様。書き終えてからランダム性が全くないことに気づいたのですがまあよしとします。

`default_nettype none

module dice(clock, reset, in, seg); 
   input clock, reset, in;
   output [6:0] seg;
   output   delayed_in;
   output [2:0] bin;
   wire [3:0] bin2;
   assign bin2 = {1'b0,bin};
 

   inputmodule inputmodule(clock, in, delayed_in);
   counter1_6 counter1_6(clock & delayed_in, reset, bin);
   bcd7seg bcd7seg(bin2, seg);
endmodule // dice


module inputmodule(clock, in, out);
   input in, clock;
   output out;
   reg 	  [31:0] q;

   assign out = in | q[31];
   
   always @(posedge in or posedge clock)
     begin
	if( in ) q <= ~32'b0;
	else q <=  q << 1;
     end
     
endmodule // inputmodule

// counter from 1 to 6
module counter1_6(clock, reset, q);
   input clock,reset;
   output [2:0] q;
   reg [2:0] 	q;

   always @(posedge clock or posedge reset)
     begin
	if( reset ) q <= 3'd1;
	else if( q == 3'd6 ) q <= 3'd1;
	else q <= q + 3'd1;
     end

endmodule // counter1_6

// binary to 7segLED
module bcd7seg(in, seg);
   input [3:0] in;
   output [6:0] seg;

   assign seg = decode(in);
   
   function [6:0] decode;
      input [3:0] in;
      begin
	 if(in == 0) decode = 7'b1111110;
	 else if(in == 1) decode = 7'b0110000;
	 else if(in == 2) decode = 7'b1101101;
	 else if(in == 3) decode = 7'b1111001;
	 else if(in == 4) decode = 7'b0110011;
	 else if(in == 5) decode = 7'b1011011;
	 else if(in == 6) decode = 7'b1011111;
	 else if(in == 7) decode = 7'b1110000;
	 else if(in == 8) decode = 7'b1111111;
	 else if(in == 9) decode = 7'b1111011;
	 else             decode = 7'b0110111;
      end
   endfunction
   
endmodule

HelloWorld代わりに書いたレベルなので実機で動くか不明& 色々変なところがあるとは思いますが、とりあえずシミュレータでは動いています。

$ iverilog main.v test.v
$ ./a.out 
		  Time:clock,in,7segout
                   0 : 0 0 => 0110000
                   5 : 1 0 => 0110000
                  10 : 0 0 => 0110000
                  15 : 1 0 => 1101101
                  20 : 0 1 => 1101101
                  25 : 1 1 => 1111001
                  30 : 0 0 => 1111001
                  35 : 1 0 => 0110011
                  40 : 0 0 => 0110011
                  45 : 1 0 => 1011011
                  50 : 0 0 => 1011011
                  55 : 1 0 => 1011111
                  60 : 0 0 => 1011111
                  65 : 1 0 => 0110000
                  70 : 0 0 => 0110000
                  75 : 1 0 => 1101101
                  80 : 0 0 => 1101101
                  85 : 1 0 => 1111001
                  90 : 0 0 => 1111001
                  95 : 1 0 => 0110011
                 100 : 0 0 => 0110011
                 105 : 1 0 => 1011011
                 110 : 0 0 => 1011011
                 115 : 1 0 => 1011111
                 120 : 0 0 => 1011111
                 125 : 1 0 => 0110000
                 130 : 0 0 => 0110000
                 135 : 1 0 => 1101101
                 140 : 0 0 => 1101101
                 145 : 1 0 => 1111001
                 150 : 0 0 => 1111001
                 155 : 1 0 => 0110011
                 160 : 0 0 => 0110011
                 165 : 1 0 => 1011011
                 170 : 0 0 => 1011011
                 175 : 1 0 => 1011111
                 180 : 0 0 => 1011111
                 185 : 1 0 => 0110000
                 190 : 0 0 => 0110000
                 195 : 1 0 => 1101101
                 200 : 0 0 => 1101101
                 205 : 1 0 => 1111001
                 210 : 0 0 => 1111001
                 215 : 1 0 => 0110011
                 220 : 0 0 => 0110011
                 225 : 1 0 => 1011011
                 230 : 0 0 => 1011011
                 235 : 1 0 => 1011111
                 240 : 0 0 => 1011111
                 245 : 1 0 => 0110000
                 250 : 0 0 => 0110000
                 255 : 1 0 => 1101101
                 260 : 0 0 => 1101101
                 265 : 1 0 => 1111001
                 270 : 0 0 => 1111001
                 275 : 1 0 => 0110011
                 280 : 0 0 => 0110011
                 285 : 1 0 => 1011011
                 290 : 0 0 => 1011011
                 295 : 1 0 => 1011111
                 300 : 0 0 => 1011111
                 305 : 1 0 => 0110000
                 310 : 0 0 => 0110000
                 315 : 1 0 => 1101101
                 320 : 0 0 => 1101101
                 325 : 1 0 => 1111001
                 330 : 0 0 => 1111001
                 335 : 1 0 => 0110011
                 340 : 0 0 => 0110011
                 345 : 1 0 => 1011011
                 350 : 0 0 => 1011011
                 355 : 1 0 => 1011011
                 360 : 0 0 => 1011011
                 365 : 1 0 => 1011011
                 370 : 0 0 => 1011011
                 375 : 1 0 => 1011011
                 380 : 0 0 => 1011011
                 385 : 1 0 => 1011011
                 390 : 0 0 => 1011011
                 395 : 1 0 => 1011011
                 400 : 0 0 => 1011011

動かせる実機は持っているのですがまだ動かしていません、動かしてみたら追記する予定。TOEICが迫っているのでこの辺で。